package com.bayss.core.service.pay;



import com.alibaba.fastjson.JSON;
import com.bayss.core.DokoServerException;
import com.bayss.core.DokoServerExceptionFactor;
import com.bayss.core.entity.order.mapper.OrderMapper;
import com.bayss.core.entity.pay.Pay;
import com.bayss.core.entity.pay.PayExample;
import com.bayss.core.entity.pay.mapper.PayMapper;
import com.bayss.core.entity.user.UserInfo;
import com.bayss.core.enums.PayOrigin;
import com.bayss.core.model.WxConfig;
import com.bayss.core.service.user.UserService;
import com.bayss.core.util.CollectionUtils;
import com.bayss.core.util.HttpUtils;
import com.bayss.core.util.PayUtil;
import com.bayss.core.util.XmlUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class PayService {

    private static Logger logger = LogManager.getLogger();

    private final PayMapper payMapper;

    private final UserService userService;

    private final OrderMapper orderMapper;

//    @Autowired
//    RedisUtil redisUtil;

    @Autowired
    public PayService(PayMapper payMapper, UserService userService, OrderMapper orderMapper) {
        this.payMapper = payMapper;
        this.userService = userService;
        this.orderMapper = orderMapper;
    }

    // 支付的总入口
    @Transactional
    public String payOrder(String openId, String orderNo, long uid, String money, PayOrigin payOrigin, String code) throws Exception {
        String payInfo = "";
        String nonceStr = PayUtil.getNonceStr();
        switch (payOrigin) {
            case O_CASH:
                payInfo = "O_CASH";
                Pay pay1 = buildPayInfo(nonceStr, orderNo, 0, payInfo, payOrigin.ordinal());

                // 订单
                if (orderNo.startsWith("x")) {
                    orderMapper.ackPayOrders(orderNo);
                } else {
                    orderMapper.ackPayOrder(orderNo);
                }
                break;
            case O_MEMBER:
                if (StringUtils.isBlank(code)) {
                    throw new DokoServerException(DokoServerExceptionFactor.VAIL_CODE_FAIL);
                }

//                Object vail = redisUtil.get(uid+"-"+code);
//                if (vail == null || (int) vail != 1) {
//                    throw new DokoServerException(DokoServerExceptionFactor.VAIL_CODE_FAIL);
//                }

                payInfo = "O_MEMBER";
                Pay pay = buildPayInfo(nonceStr, orderNo, Integer.parseInt(money), payInfo, payOrigin.ordinal());
                boolean res = userService.updateBalance(uid, -Integer.parseInt(money));
                if (!res) {
                    throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "update user balance is error");
                }
                pay.setPayStatus(1);
                PayExample payExample1 = new PayExample();
                PayExample.Criteria criteria1 = payExample1.createCriteria();
                criteria1.andOrderNoEqualTo(orderNo);
                if (payMapper.updateByExampleSelective(pay, payExample1) < 1) {
                    throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "update pay info is error");
                }
                // 订单
                if (orderNo.startsWith("x")) {
                    orderMapper.ackPayOrders(orderNo);
                } else {
                    orderMapper.ackPayOrder(orderNo);
                }
                break;
            case O_WX:
                payInfo = payInfo(nonceStr, openId, orderNo, money, payOrigin);
                break;
            case ACTIVE:
                payInfo = payInfo(nonceStr, openId, orderNo, money, payOrigin);
                break;
            case RECHARGE:
                payInfo = payInfo(nonceStr, openId, orderNo, money, payOrigin);
                break;
        }
        return payInfo;
    }

    private Pay buildPayInfo(String nonceStr, String orderNo, int money, String payInfo, int payOrigin){
        Pay pay = Pay.builder()
                .orderNo(orderNo)
                .origin(payOrigin)
                .payPrice(money)
                .payInfo(payInfo)
                .payNo(nonceStr)
                .payStatus(0)
                .build();
        if (payMapper.insertSelective(pay) < 1) {
            throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "pay insert is fail");
        }
        return pay;
    }

    // 构建微信需要的基础支付信息
    private String payInfo(String nonceStr, String openId, String orderNo, String money, PayOrigin payOrigin) throws Exception{
        Map<String, String> params = new HashMap<>();
        params.put("appid", WxConfig.appid);
        params.put("mch_id", WxConfig.mch_id);
        params.put("nonce_str", nonceStr);
        params.put("body", "doko-"+orderNo);//商品名称
        params.put("out_trade_no", orderNo); //商户订单号
        params.put("total_fee", money); //支付金额，这边需要转成字符串类型，否则后面的签名会失败
        params.put("spbill_create_ip", "120.27.69.84");//获取本机的IP地址
        params.put("notify_url", WxConfig.notify_url);
        params.put("trade_type", WxConfig.TRADETYPE);
        params.put("openid", openId);
        // 订单有效支付时效
        LocalDateTime localDateTime = LocalDateTime.now();
        params.put("time_start", localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.put("time_expire", localDateTime.plusMinutes(20L).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        String sign = PayUtil.getSign(params, WxConfig.SIGN_SECRET);
        params.put("sign", sign);

        String xml = XmlUtil.xmlFormat(params, false);
        String resXml = HttpUtils.post(WxConfig.pay_url, xml);

        if (StringUtils.isBlank(resXml)) {
            throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "pay res is error");
        }

        Map<String, String> resMap = XmlUtil.xmlParse(resXml);
        String return_code = resMap.get("return_code"); //返回状态码
        //返回给移动端需要的参数
        Map<String, String> response = new HashMap<>();
        if ("SUCCESS".equals(return_code)) {
            response.put("appId", WxConfig.appid);
            //业务结果
            String prepayId = resMap.get("prepay_id"); //返回的预付单信息
            response.put("nonceStr", nonceStr);
            response.put("package", "prepay_id="+ prepayId);
            long timeStamp = System.currentTimeMillis() / 1000;
            response.put("timeStamp", timeStamp +""); //这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
            response.put("orderNo", orderNo); //商户订单号
            response.put("total_fee", money); //商户订单号
            String stringSignTemp ="appId="+ WxConfig.appid +"&nonceStr="+ nonceStr +"&package=prepay_id="+ prepayId +"&signType="+ WxConfig.SIGNTYPE +"&timeStamp="+ timeStamp;
            //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
            String paySign = PayUtil.sign(stringSignTemp, WxConfig.SIGN_SECRET);
            response.put("paySign", paySign);
            response.put("signType", WxConfig.SIGNTYPE);

            String payInfo = JSON.toJSONString(response);
            Pay pay = Pay.builder()
                    .orderNo(orderNo)
                    .origin(payOrigin.ordinal())
                    .payPrice(Integer.parseInt(money))
                    .payInfo(payInfo)
                    .payNo(nonceStr)
                    .payStatus(0)
                    .build();
            if (payMapper.insertSelective(pay) < 1) {
                throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "pay insert is fail");
            }
            return payInfo;
        } else {
            throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "pay res is fail");
        }
    }

    // 退款 （这个一定要严谨， HttpUtils中注释了密钥，正式上线才可以放开）
    public boolean transfersPay (String openId, String orderNo, String amount) {
        UserInfo userInfo = userService.getUser(openId);
        Map<String, String> restmap = null;
        try {
            Map<String, String> parm = new HashMap<>();
            parm.put("mch_appid", WxConfig.appid);
            parm.put("mchid", WxConfig.mch_id); //商户号
            parm.put("nonce_str", PayUtil.getNonceStr()); //随机字符串
            parm.put("partner_trade_no", orderNo); //商户订单号
            parm.put("openid", openId); //用户openid oCVr20N2YLH9VQztnkZTaCj2aYYY
            parm.put("check_name", "NO_CHECK"); //校验用户姓名选项 OPTION_CHECK
            //parm.put("re_user_name", "安迪"); //check_name设置为FORCE_CHECK或OPTION_CHECK，则必填
            parm.put("amount", amount); //转账金额
            parm.put("desc", userInfo.getNickname() +" 申请提现金额："+amount+"分 " + userInfo.getId()); //企业付款描述信息
            parm.put("spbill_create_ip", "120.27.69.84"); //Ip地址
            parm.put("sign", PayUtil.getSign(parm, WxConfig.SIGN_SECRET));

            String restxml = HttpUtils.posts(WxConfig.TRANSFERS_PAY, XmlUtil.xmlFormat(parm, false));
            restmap = XmlUtil.xmlParse(restxml);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // 异常返回
        }
        if (!org.springframework.util.CollectionUtils.isEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {
            logger.info("转账成功");
            return true;
        } else {
            // 转账失败返回
        }
        return false;
    }

    // 支付成功回调
    public void ackPay(String orderNo){
        boolean res = payMapper.ackPay(orderNo) > 0;
        if (!res) {
            logger.warn("ack pay is fail order no is %s", orderNo);
        }
    }

    public Pay getPay(String orderNo){
        PayExample payExample = new PayExample();
        PayExample.Criteria criteria = payExample.createCriteria();
        criteria.andOrderNoEqualTo(orderNo);
        List<Pay> pays = payMapper.selectByExample(payExample);
        if (CollectionUtils.isEmpty(pays)) {
            throw new DokoServerException(DokoServerExceptionFactor.DEFAULT, "pay info is not exist");
        }
        return pays.get(0);
    }


}
